package com.example.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.example.anno.Log;
import com.example.constant.Constant;
import com.example.dto.BizContentDTO;
import com.example.mapper.EventMapper;
import com.example.mapper.SeatMapper;
import com.example.mapper.TicketMapper;
import com.example.pojo.*;
import com.example.properties.AliPayProperties;
import com.example.properties.SystemProperties;
import com.example.service.TicketService;
import com.example.service.UserService;
import com.example.utils.AliSMS;
import com.example.utils.PathUtil;
import com.example.vo.EventVO;
import com.example.vo.OrderVO;
import com.example.vo.PayStatusVO;
import com.example.vo.TicketVO;
import com.github.pagehelper.PageHelper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Log4j2
public class TicketServiceImpl implements TicketService {

    @Autowired
    private TicketMapper ticketMapper;

    @Autowired
    private EventMapper eventMapper;

    @Autowired
    private SeatMapper seatMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private AliSMS aliSMS;

    @Autowired
    private AliPayProperties aliPayConf;

    @Autowired
    private SystemProperties sysConf;

    @Log
    @Override
    public Result save(TicketVO ticketVO) {
        if (CollectionUtils.isEmpty(ticketVO.getSeatID())) {
            return Result.error("座位ID不能为空");
        }
        if (!validateSeat(ticketVO)) {
            return Result.error("座位已出售");
        }
        // 查询event
        Event event = eventMapper.getByID(ticketVO.getEventID());
        // 生成订单号
        Date nowDate = new Date();
        String orderNum = DateUtil.format(nowDate, DatePattern.PURE_DATETIME_MS_PATTERN) + event.getEventID() + String.format("%03d", ticketVO.getSeatID().get(0));
        // 总价格
        BigDecimal totalPrice = ticketVO.getTicketPrice().multiply(new BigDecimal(ticketVO.getSeatID().size()));
        // 支付宝支付
        String privateKey = FileUtil.readUtf8String(PathUtil.getResourcesPath() + "alipay_private_key.txt");
        String publicKey = FileUtil.readUtf8String(PathUtil.getResourcesPath() + "alipay_public_key.txt");
        AlipayClient alipayClient = new DefaultAlipayClient(aliPayConf.getServerUrl(), aliPayConf.getAppId(), privateKey, "json", "utf-8", publicKey, "RSA2");
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        // 由于系统需要部署到具有公网的服务器上，支付宝才可以回调本系统的接口，所以暂时用不到NotifyUrl
        request.setReturnUrl(sysConf.getPayCallbackUrl() + "/pay/result/" + orderNum);
        // 设置请求参数
        BizContentDTO bizContentDTO = new BizContentDTO();
        bizContentDTO.setSubject("票务购买")
                .setOutTradeNo(orderNum)
                .setTotalAmount(totalPrice).setProductCode("FAST_INSTANT_TRADE_PAY");
        request.setBizContent(JSON.toJSONString(bizContentDTO));
        String form = "";
        try {
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                form = response.getBody();
            } else {
                log.error("发送支付请求失败！");
                return Result.error("发送支付请求失败！");
            }
        } catch (AlipayApiException e) {
            log.error("发送支付请求发生异常！", e);
            return Result.error("发送支付请求发生异常！");
        }
        // 订单入库
        ticketVO.getSeatID().forEach(seatId -> {
            Ticket ticket = new Ticket();
            //将ticketVO中相同字段copy到ticket中
            BeanUtils.copyProperties(ticketVO, ticket);
            ticket.setSeatID(seatId).setStatus("待支付").setOrderNum(orderNum)
                    .setCreateTime(LocalDateTime.now());
            ticketMapper.save(ticket);
        });
        return Result.success(form);
    }

    private boolean validateSeat(TicketVO ticketVO){
        // 从redis中查询是否存在该座位lua脚本检验，验证座位是否存在已售出的情况
        Boolean isExistPurchasedSeat = redisService.sExeLuaDiffForSeat(String.valueOf(ticketVO.getEventID()), ticketVO.getSeatID());
        if (Boolean.TRUE.equals(isExistPurchasedSeat)) {
            return Boolean.FALSE;
        }
        // 数据库再次验证座位是否已出售
        int res =  ticketMapper.selectSeatIsSold(ticketVO.getEventID(), ticketVO.getSeatID());
        // 都未售出，则将本单的座位存入redis
        if (res == 0) {
            redisService.sSet(String.valueOf(ticketVO.getEventID()), ticketVO.getSeatID().toArray());
        }
        return res == 0;
    }

    @Log
    @Override
    public Result delete(Integer ticketID) {
        Ticket ticket = ticketMapper.selectById(ticketID);
        if (ticket == null){
            return Result.error("当前订单不存在！");
        }
        //下面都是同一订单编号
        // 查出所有这个订单号的所有订单
        List<Ticket> seatList = ticketMapper.selectTicketsByOrderNum(ticket.getOrderNum());
        // 获取总价格
        final BigDecimal[] bigDecimal = {new BigDecimal(0)};
        seatList.forEach(ticket1 -> bigDecimal[0] = bigDecimal[0].add(ticket1.getTicketPrice()));
        BigDecimal totalPrice = bigDecimal[0].setScale(2, RoundingMode.DOWN);
        // 支付宝支付
        String privateKey = FileUtil.readUtf8String(PathUtil.getResourcesPath() + "alipay_private_key.txt");
        String publicKey = FileUtil.readUtf8String(PathUtil.getResourcesPath() + "alipay_public_key.txt");
        AlipayClient alipayClient = new DefaultAlipayClient(aliPayConf.getServerUrl(),aliPayConf.getAppId(),privateKey,"json","utf-8",publicKey,"RSA2");
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        JSONObject bizContent = new JSONObject();
        //
        bizContent.put("trade_no", ticket.getTradeNo());
        // 退款退90%
        bizContent.put("refund_amount", totalPrice.multiply(BigDecimal.valueOf(0.9)));
        bizContent.put("out_request_no", "HZ01RF001");
        request.setBizContent(bizContent.toString());
        try{
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if(response.isSuccess()){
                if (StringUtils.equals(response.getCode(), "10000")) {
                    ticketMapper.updateStatus(ticket.getOrderNum(), ticket.getTradeNo(), "已退款");

                    // redis删除已售卖的座位号
                    redisService.setRemove(String.valueOf(ticket.getEventID()), seatList.stream().map(Ticket::getSeatID).toArray());
                }
            } else {
               return Result.error("退款失败！");
            }
        } catch (AlipayApiException e) {
            log.error("支付退款请求发生异常！", e);
            return Result.error("支付退款请求发生异常！");
        }
        return Result.success("退款成功！");
    }

    @Override
    public PageBean list(Integer userID, Integer eventID, Integer ticketID, String status, Integer page, Integer pageSize) {
        //1.设置分页参数
        int startIdx = (page-1)*pageSize;
        //2.查询数据
        List<Ticket> ticketList = ticketMapper.listAll(userID, eventID, ticketID, status, startIdx, pageSize);
        long count = ticketMapper.listAllCount(userID, eventID, ticketID, status, startIdx, pageSize);
        if (CollectionUtils.isEmpty(ticketList)) {
            return new PageBean();
        }
        // 获取 event
        Event event = eventMapper.getByID(eventID);
        List<OrderVO> orderVOList = getTicketList(ticketList, event);
        //3.封装PageBean对象
        return new PageBean(count, orderVOList);
    }

    private List<OrderVO> getTicketList(List<Ticket> ticketList, Event event){
        List<OrderVO> orderVOList = new ArrayList<>();
        // 按照订单编号分组
        Map<String, List<Ticket>> groupTickets = ticketList.stream()
                .collect(Collectors.groupingBy(Ticket::getOrderNum));
        // 根据订单编号封装
        for (Map.Entry<String, List<Ticket>> entry : groupTickets.entrySet()) {
            String orderNum = entry.getKey();
            List<Ticket> tickets = entry.getValue();
            if (event == null || event.getEventID() != tickets.get(0).getEventID()) {
                event = eventMapper.getByID(tickets.get(0).getEventID());
            }
            OrderVO orderVO = new OrderVO();
            String seatPrefix = StringUtils.EMPTY;
            if (tickets.get(0).getTicketPrice().compareTo(BigDecimal.valueOf(event.getLowPrice())) == 0 ) {
                seatPrefix = "1-";
            }
            if (tickets.get(0).getTicketPrice().compareTo(BigDecimal.valueOf(event.getMidPrice())) == 0 ) {
                seatPrefix = "2-";
            }
            if (tickets.get(0).getTicketPrice().compareTo(BigDecimal.valueOf(event.getHighPrice())) == 0 ) {
                seatPrefix = "3-";
            }
            List<String> seatInfo = new ArrayList<>();
            // 总价
            final BigDecimal[] totalPrice = {new BigDecimal(0)};
            String finalSeatPrefix = seatPrefix;
            tickets.forEach(ticket -> {
                totalPrice[0] = totalPrice[0].add(ticket.getTicketPrice());
                Seat seat = seatMapper.getSeatByID(ticket.getSeatID());
                seatInfo.add(finalSeatPrefix + seat.getSeatNum());
            });
            BigDecimal scaleTotalPrice = totalPrice[0].setScale(2, RoundingMode.DOWN);
            orderVO.setTicketIDs(tickets.stream().map(Ticket::getTicketID).collect(Collectors.toList()))
                    .setOrderNum(orderNum).setCreatTime(tickets.get(0).getCreateTime())
                    .setTicketCount(tickets.size())
                    .setStatus(tickets.get(0).getStatus())
                    .setTicketPrice(scaleTotalPrice.toString())
                    .setSeatInfo(seatInfo);
            // 封装用户信息，每个订单编号下的用户id都是一样的
            User user = userService.getById(tickets.get(0).getUserID());
            orderVO.setUserID(user.getUserID()).setUsername(user.getUsername());
            // 封装赛事
            EventVO eventVO = new EventVO();
            eventVO.setEventID(event.getEventID())
                    .setEventName(event.getEventName()).setStartTime(event.getStartTime())
                    .setEndTime(event.getEndTime()).setImage(event.getImage())
                    .setPlace(event.getPlace()).setLowPrice(event.getLowPrice())
                    .setMidPrice(event.getMidPrice()).setHighPrice(eventVO.getHighPrice())
                    .setOnSale(event.getOnSale());
            orderVO.setEventInfo(eventVO);
            orderVOList.add(orderVO);
        }
        return orderVOList;
    }

    @Override
    public String parseNotify(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>();
        //支付返回结果都封装在 Request 作用域中。所以要对 request 进行解析
        Map<String, String[]> requestParams = request.getParameterMap();
        for (Map.Entry<String, String[]> stringEntry : requestParams.entrySet()) {
            String name = stringEntry.getKey();
            String[] values = stringEntry.getValue();
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            params.put(name, valueStr);
        }
        // 支付宝支付
        String publicKey = FileUtil.readUtf8String(new ClassPathResource("static/alipay_public_key.txt").getPath());
        //验签
        try {
            boolean signVerified = AlipaySignature.rsaCheckV1(params, publicKey, "utf-8", "RSA2");
            if (signVerified) {
                // 支付成功，更新订单支付状态
                ticketMapper.updateStatus(params.get("trade_no"), null, "生效");
                return Constant.ALIPAY_REP_SUCCESS;
            }
        } catch (AlipayApiException e) {
            log.error("验签失败！", e);
            return Constant.ALIPAY_REP_FAILURE;
        }
        return Constant.ALIPAY_REP_FAILURE;
    }

    @Override
    public Result payStatusOk(PayStatusVO payStatusVO) {
        // 支付成功，更新订单支付状态
        ticketMapper.updateStatus(payStatusVO.getOrderNum(), payStatusVO.getTradeNo(),"生效");
        String phoneNumber = ticketMapper.selectUserPhoneNumberByOrderNum(payStatusVO.getOrderNum());
        if (StringUtils.isBlank(phoneNumber)) {
            return Result.success();
        }
        try{
            aliSMS.payOrder(phoneNumber, payStatusVO.getOrderNum());
            return Result.success();
        }catch (Exception e) {
            log.error(e);
            return Result.success();
        }
    }
}
